#include <math.h>
#include <memory>
#include "Blocks.h"
#include "BlocksConsts.h"
#include "CanInterface.h"

Block::Block(CanInterface* can, unsigned char figIndex, unsigned char rotIndex) 
: _can(can)
, _figIndex(figIndex)
, _rotIndex(rotIndex)
, _color(figIndex + 1)
{};

void Block::MoveLeft()
{
	if (_CheckCells(-1, 0))
	{
		_UpdateMyCells(CELL_FREE);
		
		for (unsigned index = 0; index < FIGURE_COMPLEXITY; ++index)
			--_cell[index].x;

		_UpdateMyCells(_color);
	}
}

void Block::MoveRight()
{
	if (_CheckCells(1, 0))
	{
		_UpdateMyCells(CELL_FREE);
		
		for (unsigned index = 0; index < FIGURE_COMPLEXITY; ++index)
			++_cell[index].x;

		_UpdateMyCells(_color);
	}
}

bool Block::MoveDown()
{
	bool canMove = _CheckCells(0, -1);

	if (canMove)
	{
		_UpdateMyCells(CELL_FREE);
		
		for (unsigned index = 0; index < FIGURE_COMPLEXITY; ++index)
			--_cell[index].y;

		_UpdateMyCells(_color);
	}
	else
	{
		_UpdateMyCells(_color | CELL_OCCUPIED);
	}

	return canMove;
}

void Block::FallDown()
{
	unsigned char fallDistance = 0;

	while(_CheckCells(0, -(fallDistance + 1)))
		++fallDistance;

	if (fallDistance)
	{	
		_UpdateMyCells(CELL_FREE);
			
		for (unsigned index = 0; index < FIGURE_COMPLEXITY; ++index)
			_cell[index].y -= fallDistance;
		
		_UpdateMyCells(_color | CELL_OCCUPIED);
	}
}

void Block::Rotate()
{
	Block rotatedBlock  = *this;	
	unsigned char rotIndex = (_rotIndex + 1) % ROTATIONS;

	char base_x = _cell[0].x - _blocks[_figIndex][_rotIndex][0];
	char base_y = _cell[0].y - _blocks[_figIndex][_rotIndex][1];

	for (int i = 0; i < FIGURE_COMPLEXITY; ++i)
	{
		rotatedBlock._cell[i].y = base_y + _blocks[_figIndex][(_rotIndex + 1) % 4][i*2 + 1];
		rotatedBlock._cell[i].x = base_x + _blocks[_figIndex][(_rotIndex + 1) % 4][i*2];
	}
	
	Cell data[FIGURE_COMPLEXITY];
 	memcpy(data, _cell, sizeof(Cell)*FIGURE_COMPLEXITY);
	memcpy(_cell, rotatedBlock._cell, sizeof(Cell)*FIGURE_COMPLEXITY);

	if (_CheckCells(0, 0))
	{
		_rotIndex = rotIndex;

		memcpy(_cell, data, sizeof(Cell)*FIGURE_COMPLEXITY);
		_UpdateMyCells(CELL_FREE);

		memcpy(_cell, rotatedBlock._cell, sizeof(Cell)*FIGURE_COMPLEXITY);
		_UpdateMyCells(_color);
	}
	else
	{
		memcpy(_cell, data, sizeof(Cell)*FIGURE_COMPLEXITY);
	}
}

bool Block::_CheckCells(int x_offs, int y_offs)
{
	bool destCellsFree = true;

	for (unsigned index = 0; destCellsFree && index < FIGURE_COMPLEXITY; ++index)
	{
		if (_can->ReadCell(_cell[index].x + x_offs, _cell[index].y + y_offs) & CELL_OCCUPIED)
			destCellsFree = false;
	}

	return destCellsFree;
} 

void Block::_UpdateMyCells(unsigned char value)
{
	for (unsigned index = 0; index < FIGURE_COMPLEXITY; ++index)
		_can->Cell(_cell[index].x, _cell[index].y) = value;
}

void Block::CheckLines()
{
	unsigned upper = _cell[0].y;
	unsigned lower = _cell[0].y;

	for (unsigned index = 1; index < FIGURE_COMPLEXITY; ++index)
	{
		if (_cell[index].y > upper) upper = _cell[index].y;
		if (_cell[index].y < lower) lower = _cell[index].y;
	}

	_can->CheckLines(lower, upper);
}

void Block::PutOnTop()
{
	for (unsigned cell = 0; cell < FIGURE_COMPLEXITY; ++cell)
	{
		_cell[cell].x += (_can->Width() - 1) >> 1; // -1 substitutes "floor" function
		_cell[cell].y += _can->RedLine();
	}

	_UpdateMyCells(_color);
}

void Block::Update(unsigned char figIndex, unsigned char rotIndex)
{
	_figIndex = figIndex;
	_rotIndex = rotIndex;
	_color	  = figIndex + 1;

	for (unsigned cell = 0; cell < FIGURE_COMPLEXITY; ++cell)
	{
		_cell[cell].x = _blocks[_figIndex][_rotIndex][2*cell];
		_cell[cell].y = _blocks[_figIndex][_rotIndex][2*cell + 1];
	}
}
